﻿using DoNetDrive.Connector.WindowsBLE.Status;
using DoNetDrive.Core.Connector;
using DotNetty.Buffers;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.PortableExecutable;
using System.Runtime.InteropServices.JavaScript;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.PointOfService.Provider;
using Windows.Security.Cryptography;
using Windows.Web;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace DoNetDrive.Connector.WindowsBLE
{
    public class BLEConnector : AbstractConnector
    {
        private ulong _BLEAddr = 0;


        /// <summary>
        /// 当前连接的蓝牙设备
        /// </summary>
        public BluetoothLEDevice CurrentDevice { get; private set; }

        /// <summary>
        /// 当前连接的服务
        /// </summary>
        public GattDeviceService CurrentService { get; private set; }

        /// <summary>
        /// 写特征对象
        /// </summary>
        public GattCharacteristic CurrentWriteCharacteristic { get; set; }

        /// <summary>
        /// 通知特征对象
        /// </summary>
        public GattCharacteristic CurrentNotifyCharacteristic { get; set; }


        /// <summary>
        /// 特性通知类型通知启用
        /// </summary>
        private const GattClientCharacteristicConfigurationDescriptorValue CHARACTERISTIC_NOTIFICATION_TYPE =
            GattClientCharacteristicConfigurationDescriptorValue.Notify;


        public BLEConnector(BLEDetail dtl)
        {
            base._ConnectorDetail = dtl;
            _BLEAddr = dtl.BluetoothAddress;

        }
        protected override INConnectorStatus GetInitializationStatus()
        {
            return BLEStatus.Closed;
        }

        public override IPDetail LocalAddress()
        {
            return new IPDetail(_BLEAddr.ToString(), 0);
        }

        public override IPDetail RemoteAddress()
        {
            return new IPDetail(_BLEAddr.ToString(), 0);
        }
        public override string GetConnectorType()
        {
            return BLEDetail.BLETypeName;
        }

        #region 连接蓝牙设备
        public override async Task ConnectAsync()
        {
            if (CheckIsInvalid()) return;
            if (IsActivity()) return;
            _IsActivity = false;
            _Status = BLEStatus.Connecting;

            BLEDetail dtl = base._ConnectorDetail as BLEDetail;
            if (dtl == null)
            {
                SetInvalid();
                await Task.FromException(new Exception("BLEDetail is null"));

                return;
            }
            //发送准备连接通知
            FireConnectorConnectingEvent(GetConnectorDetail());

            try
            {
                //根据蓝牙地址，获取蓝牙对象
                CurrentDevice = await BluetoothLEDevice.FromBluetoothAddressAsync(dtl.BluetoothAddress);
                if(CurrentDevice == null)
                {
                    SetInvalid();
                    await Task.FromException(new Exception("No find bluetooth device"));

                    return;
                }
                CurrentDevice.ConnectionStatusChanged += this.CurrentDevice_ConnectionStatusChanged;

                BLESearchService.AddBLEDevice(CurrentDevice);

                //获取蓝牙服务
                var ServicesResult = await CurrentDevice.GetGattServicesAsync();
                if (ServicesResult != null)
                {
                    if (string.IsNullOrEmpty(dtl.ServiceUUID))

                        CurrentService = ServicesResult.Services.FirstOrDefault();
                    else
                        CurrentService = ServicesResult.Services.Where((x) => x.Uuid.ToString().StartsWith(dtl.ServiceUUID)).FirstOrDefault();

                }

                if (CurrentService == null)
                {
                    Release0();
                    SetInvalid();
                    await Task.FromException(new Exception("No find gattservices"));

                    return;
                }

                //查询蓝牙服务中的写通道和读通道
                var CharacteristicResult = await CurrentService.GetCharacteristicsAsync();

                if (CharacteristicResult != null)
                {
                    if (CharacteristicResult.Status == GattCommunicationStatus.Success)
                    {
                        BLESearchService.AddBLECharacteristic(CurrentDevice, CurrentService, CharacteristicResult.Characteristics.ToList());

                        foreach (GattCharacteristic gattCharacteristic in CharacteristicResult.Characteristics)
                        {
                            await SetOpteron(gattCharacteristic);
                        }
                    }
                    else if(CurrentService.Session.SessionStatus == GattSessionStatus.Active)
                    {
                        var lst = BLESearchService.GetBLECharacteristicByCatch(CurrentDevice, CurrentService);
                        if(lst != null)
                        {
                            foreach (GattCharacteristic gattCharacteristic in lst)
                            {
                                await SetOpteron(gattCharacteristic);
                            }
                        }
                    }
                }

                if (this.CurrentWriteCharacteristic == null)
                {
                    Release0();
                    SetInvalid();
                    await Task.FromException(new Exception("No find write characteristic"));

                    return;
                }

                if (this.CurrentNotifyCharacteristic == null)
                {
                    Release0();
                    SetInvalid();
                    await Task.FromException(new Exception("No find notify characteristic"));

                    return;
                }
                _Status = BLEStatus.Connected;
                _IsActivity = true;

                FireConnectorConnectedEvent(GetConnectorDetail());
                CheckCommandList();
            }
            catch (Exception ex)
            {
                if (IsInvalid)
                {
                    _ConnectorDetail.SetError(ex);
                    FireConnectorErrorEvent(GetConnectorDetail());
                    await Task.FromException(ex);
                }
                else
                {
                    Release0();

                    SetInvalid();
                    var newEx = new Exception("Connect error", ex);
                    _ConnectorDetail.SetError(newEx);
                    FireConnectorErrorEvent(GetConnectorDetail());
                    await Task.FromException(newEx);
                }


                return;
            }

        }


        /// <summary>
        /// 获取操作
        /// </summary>
        /// <returns></returns>
        private async Task SetOpteron(GattCharacteristic gattCharacteristic)
        {
            gattCharacteristic.ProtectionLevel = GattProtectionLevel.Plain;

            int isWrite = (int)gattCharacteristic.CharacteristicProperties & (int)GattCharacteristicProperties.Write;
            int isWriteWithoutResponse = (int)gattCharacteristic.CharacteristicProperties & (int)GattCharacteristicProperties.WriteWithoutResponse;
            if (isWrite > 0 || isWriteWithoutResponse > 0)
            {
                this.CurrentWriteCharacteristic = gattCharacteristic;

            }

            int isNotify = (int)gattCharacteristic.CharacteristicProperties & (int)GattCharacteristicProperties.Notify;
            if (isNotify > 0)
            {
                this.CurrentNotifyCharacteristic = gattCharacteristic;
                this.CurrentNotifyCharacteristic.ValueChanged += Characteristic_ValueChanged;
                var StatusRet = await CurrentNotifyCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(CHARACTERISTIC_NOTIFICATION_TYPE);
                if (StatusRet == GattCommunicationStatus.Success)
                {
                    //读通知绑定成功
                }
                else
                {
                    await Task.FromException(new Exception("WriteClientCharacteristicConfigurationDescriptorAsync error"));
                }
            }

        }


        private void CurrentDevice_ConnectionStatusChanged(BluetoothLEDevice sender, object args)
        {
            if (sender.ConnectionStatus == BluetoothConnectionStatus.Disconnected)
            {
                CloseAsync();
            }
        }
        #endregion


        #region 接收数据
        /// <summary>
        /// 接收到蓝牙设备发来的数据
        /// </summary>
        private void Characteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
        {
            if (_isRelease) return;
            if (this._Status.Status() != BLEStatus.Connected.Status()) return;

            byte[] data;
            CryptographicBuffer.CopyToByteArray(args.CharacteristicValue, out data);
            var byteBuf = Unpooled.WrappedBuffer(data);
            ReadByteBuffer(byteBuf);
            byteBuf.Release();
            byteBuf = null;
        }
        #endregion

        #region 发送数据
        protected override async Task WriteByteBuf0(IByteBuffer buf)
        {
            if (_isRelease) return;
            if (this._Status.Status() != BLEStatus.Connected.Status()) return;

            try
            {

                if (CommandSendIntervalTimeMS > 0)
                {
                    var iDelayTime = 0;
                    if (LastReadDataTime > LastSendDataTime)
                    {
                        iDelayTime = (DateTime.Now - LastReadDataTime).Milliseconds;
                    }
                    else
                    {
                        iDelayTime = (DateTime.Now - LastSendDataTime).Milliseconds;
                    }
                    if (iDelayTime < CommandSendIntervalTimeMS)
                    {
                        iDelayTime = CommandSendIntervalTimeMS - iDelayTime;
                        await Task.Delay(iDelayTime);
                    }
                }

                if (CurrentWriteCharacteristic != null)
                {

                    byte[] sendBuf = new byte[buf.ReadableBytes];
                    int readIndex = buf.ReaderIndex;
                    buf.ReadBytes(sendBuf, 0, buf.ReadableBytes);
                    buf.SetReaderIndex(readIndex);

                    await CurrentWriteCharacteristic.WriteValueAsync(CryptographicBuffer.CreateFromByteArray(sendBuf), GattWriteOption.WriteWithResponse);

                    return;
                }
            }
            catch (Exception ex)
            {

                var dtl = GetConnectorDetail();
                dtl.SetError(ex);
                FireConnectorErrorEvent(dtl);
            }

            await CloseAsync();
        }
        #endregion


        public override async Task CloseAsync()
        {
            if (CheckIsInvalid()) return;
            _IsActivity = false;

            Release0();

            if (IsInvalid) return;
            UpdateActivityTime();

            FireConnectorClosedEvent(_ConnectorDetail);

            SetInvalid();

            _IsForcibly = false;
            await Task.CompletedTask;
        }

        protected override void Release0()
        {
            if (CurrentNotifyCharacteristic != null)
                CurrentNotifyCharacteristic.ValueChanged -= Characteristic_ValueChanged;

            CurrentNotifyCharacteristic = null;
            CurrentWriteCharacteristic = null;

            //CurrentService?.Dispose();
            CurrentService = null;



            //CurrentDevice?.Dispose();
            if (CurrentDevice != null)
            {
                CurrentDevice.ConnectionStatusChanged -= this.CurrentDevice_ConnectionStatusChanged;
            }
            CurrentDevice = null;

        }



        internal void CheckConnectedStatus()
        {
            if (!CheckIsInvalid())
            {
                CheckCommandList();
            }
        }
    }
}
